#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <functional>
#include "Epoller.hpp"
#include "Comm.hpp"
#include "Log.hpp"
#include "Socket.hpp"
#include "nocopy.hpp"
#include "TimeManager.hpp"

class Connection;
class TcpServer;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);   // 边缘触发方式 读事件
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET); // 边缘触发方式 写事件
const static int g_buffer_size = 128;      // 事件管理器中缓冲区大小
const uint16_t defaultport = 8080;

// 设置回调函数形式
using func_t = std::function<void(std::shared_ptr<Connection>)>;


// 通过 Connection 类实现
// 1. 封装连接信息 2. 回调机制 3. 与服务器关联 4. 资源管理
class Connection
{
public:
    Connection(int sock, std::shared_ptr<TcpServer> tcp_server_ptr) 
        :_sock(sock),
        _tcp_server_ptr(tcp_server_ptr)
    {}

    // 获取套接字
    int SockFd() { return _sock; }
    
    // 向输入缓冲区添加
    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }

    // 向输出缓冲区添加
    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }

    // 获取输入缓冲区内容
    std::string &Inbuffer()
    {
        return _inbuffer;
    }

    // 获取输出缓冲区内容
    std::string &Outbuffer()
    {
        return _outbuffer;
    }

    // 设置实例的回调函数
    void SetHandler(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    
    ~Connection() {}
private:
    int _sock;                // 套接字描述符
    std::string _inbuffer;    // 输入缓冲区
    std::string _outbuffer;   // 输出缓冲区
    
public:
    func_t _recv_cb;     // 读事件回调函数
    func_t _send_cb;     // 写件回调函数
    func_t _except_cb;   // 异常事件回调函数
    
    // 回指指针
    std::shared_ptr<TcpServer> _tcp_server_ptr;

    std::string _ip;
    uint16_t _port;

    time_t last_active_time;
};

class TcpServer : public nocopy
{
    static const int num = 64; // epoll_create 的参数
public:
    TcpServer(uint16_t port, func_t OnMessage)
        :_port(port),
        _epoller_ptr(new Epoller()),
        _listensock_ptr(new Sock()),
        _OnMessage(OnMessage)
    {}

    void Init() 
    {
        _listensock_ptr->Socket();
        SetNonBlockOrDie(_listensock_ptr->Fd()); // ET 模式需要将套接字设置成非阻塞模式
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();

        lg(Info, "create listen socket success, sockfd: %d", _listensock_ptr->Fd());

        AddConnection(_listensock_ptr->Fd(), EVENT_IN, 
                        // Accepter 作为成员函数, 第一个参数必须是 this, 这之后我们需要再传入一个该函数自己的参数
                        // 将传入的参数传递到 Accepter 函数的参数, 然后调用 
                        std::bind(&TcpServer::Accepter, this, std::placeholders::_1),
                        nullptr, 
                        nullptr);
    }

    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb,
                         const std::string &ip = "0.0.0.0", uint16_t port = 8081)
    {
        // 将 fd 和 event 加到内核中
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, event);

        // 给 sock 建立一个 connection 对象, 将 sock 添加到 Connection 中
        std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sock, std::shared_ptr<TcpServer>(this));
        new_connection->SetHandler(recv_cb, send_cb, except_cb);
        new_connection->_ip = ip;
        new_connection->_port = port;

        // 同时, 将 sock 和 Connecion 放入 _connections
        _connections.insert(std::make_pair(sock, new_connection));

        // 再给每一个链接添加一个定时器
        Timer timer;
        time_manager.push(timer);

        lg(Debug, "add a new connection success, sockfd is: %d" , sock);
    }

    // 监听套接字的读事件处理函数(链接管理器) 
    void Accepter(std::shared_ptr<Connection> connection)
    {
        while(true)
        {
            // 获取客户端信息
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 调用全局命名空间中的 accept 函数，而不是当前类或命名空间中的任何同名函数
            int sock = ::accept(connection->SockFd(), (struct sockaddr*)&peer, &len);
            if(sock > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr, ipbuf, sizeof(ipbuf));
                lg(Debug, "get a new client, client info -> [%s:%d], sockfd: %d" , ipbuf, peerport, sock);

                // ET 模式需要将套接字设置成非阻塞模式
                SetNonBlockOrDie(sock); 
                // listensock 只需要设置 _recv_cb，而一般 sock 的读，写，异常指针都要设置
                AddConnection(sock, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1),
                              ipbuf, peerport); 
            }
            else
            {
                if(errno == EWOULDBLOCK) break;
                else if(errno == EINTR) continue;
                else break;
            }
        }
    }

    // 读事件管理器
    // 服务器不用关心数据的格式, 只要IO数据就行, 至于有没有读完, 报文的具体格式细节, 服务器都不用管
    void Recver(std::shared_ptr<Connection> connection)
    {
        connection->last_active_time = time(nullptr);

        int sock = connection->SockFd();
        while(true)
        {
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer)-1, 0); // 非阻塞读取
            if(n > 0)
            {
                connection->AppendInBuffer(buffer);
            }
            else if(n == 0)
            {
                lg(Info, "sockfd: %d, client info [%s:%d] quit...", connection->_ip.c_str(), connection->_port);
                connection->_tcp_server_ptr->Excepter(connection);
            }
            else
            {
                if (errno == EWOULDBLOCK) break;
                else if (errno == EINTR) continue;
                else 
                {
                    lg(Warning, "sockfd: %d, client info [%s:%d] recv error...", sock, connection->_ip.c_str(), connection->_port);
                    connection->_tcp_server_ptr->Excepter(connection);
                    break;
                }
            }
        }
        // 数据有了, 但是不一定是完整的
        // 因此我们需要 1. 检测 2. 如果有完整报文，就处理
        _OnMessage(connection);
    }
    
    // 写事件管理器
    void Sender(std::shared_ptr<Connection> connection) 
    {
        connection->last_active_time = time(nullptr);

        int sock = connection->SockFd();
        auto &outbuffer = connection->Outbuffer();
        while(true)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if(n > 0)
            {
                outbuffer.erase(0, n);
                if (outbuffer.empty()) break;
            }
            else if(n == 0)
            {
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK) break;
                else if (errno == EINTR) continue;
                else 
                {
                    lg(Warning, "sockfd: %d, client info [%s:%d] send error...", sock, connection->_ip.c_str(), connection->_port);
                    connection->_tcp_server_ptr->Excepter(connection);
                    break;
                }
            }
        }

        if (!outbuffer.empty()) // 对写事件进行关心
        {
            EnableEvent(connection->SockFd(), true, true);
        }
        else // 对写事件取消关心
        {
            EnableEvent(connection->SockFd(), true, false);
        }

    }
    
    // 异常事件管理器
    void Excepter(std::shared_ptr<Connection> connection) 
    {
        lg(Warning, "Excepter hander sockfd: %d, client info [%s:%d] excepter handler", connection->SockFd(), connection->_ip.c_str(), connection->_port);

        // 验证 sock 是否合法
        if (!IsConnectionSafe(connection->SockFd())) return;

        //1．移除对特定fd的关心
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, connection->SockFd(), 0);

        // 2．关闭异常的文件描述符
        lg(Debug, "close %d done!", connection->SockFd());
        close(connection->SockFd());
        
        // 3．从unordered_map中移除
        lg(Debug, "remove %d from _connections!", connection->SockFd());
        _connections.erase(connection->SockFd());
    }

    // 对事件的状态进行设置
    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, events);
    }

    // 判断 fd 是否被用户关心(是否存在 epoll 模型的红黑树中)
    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
        {
            return false;
        }
        else
        {            
            return true;
        }
    }

    // 任务派发器
    void Dispatcher(int timeout)
    {
        // 查询当前有多少的事件就绪
        int n = _epoller_ptr->EpollerWait(revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events;
            int sock = revs[i].data.fd;
            //统一把事件异常转换成为读写问题
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);
            // 只处理 EPOLLIN 和 EPOLLOUT
            if ((events & EPOLLIN) && IsConnectionSafe(sock))
            {
                if(_connections[sock]->_recv_cb)
                    _connections[sock]->_recv_cb(_connections[sock]);
            }
            if ((events & EPOLLOUT) && IsConnectionSafe(sock))
            {
                if(_connections[sock]->_send_cb)
                    _connections[sock]->_send_cb(_connections[sock]);
            }
        }
    }
    
    // 在任务派发后的空闲期所做的其他事
    void do_other_thing()
    {
        // 检测最小堆 top(); 是否超时
        // 没有超时: int timeout = top()->expired - now();
        // 如果超时：top()->cb(); pop(); 
    }

    void Loop() 
    {
        _quit = false;

        // 我们添加对应的事件时, 除了要将 fd 和 event 加到内核中
        // 还要给 listensock 建立一个 connection 对象, 将 listensock 添加到 Connection 中
        // 同时, 将 listensock 和 Connecion 放入 _connections
        // AddConnection(); -> 移至初始化函数

        while (!_quit)
        {
            Dispatcher(1000);

            do_other_thing();
        }

        _quit = true;
    }
    
    ~TcpServer() {}
private:
    std::shared_ptr<Epoller> _epoller_ptr;                              // epoll 句柄指针
    std::shared_ptr<Sock> _listensock_ptr;                              // 监听套接字指针
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;  // <sock, Connection> 对，表示每一个 sock 与其对应的各个处理方案
    struct epoll_event revs[num];                                       // epoll_wait 函数参数
    uint16_t _port;                                                     // 端口号
    bool _quit;                                                         // 服务器是否退出

    TimeManager time_manager;                                           // 时间管理确认

    func_t _OnMessage;                                                  // 上层处理函数
    // std::vector<int> fds;                                            // 也可以使用 vector 数组来管理众多 fd
};
